#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include <unistd.h> 
#include <sys/socket.h>    /* basic socket definitions */
#include <netinet/in.h>    /* sockaddr_in{} and other Internet defns */
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/time.h>

#include "ikcp.h"
#include "common/memAlloca.h"

#define SERV_PORT 23337
#define RECV_BUF_LEN (1024*16)
#define KCP_CONV 0x11223344


#ifndef WIN32
int daemonize(const char *dir)
{
	printf("daemonize ...\n");
	switch(fork()){
		case -1:
			printf("fork() return -1\n");
			exit(-1);
		case 0:
			break;
		default:
			printf("parent exit() normally\n");
			exit(0);
	}
	if(setsid() == -1){
		exit(0);
	}
	if(dir != NULL){
		if(chdir(dir) == -1){
			exit(0);
		}
	}
	


	if(close(STDIN_FILENO) == -1){
		exit(0);
	}
	if(close(STDOUT_FILENO) == -1){
		exit(0);
	}
	if(close(STDERR_FILENO) == -1){
		exit(0);
	}

	int fd = open("/dev/null", O_RDWR, 0);
	if(fd == -1){
		exit(0);
	}
	if(dup2(fd, STDIN_FILENO) == -1){
		exit(0);
	}
	if(dup2(fd, STDOUT_FILENO) == -1){
		exit(0);
	}
	if(dup2(fd, STDERR_FILENO) == -1){
		exit(0);
	}


	return 0;
}
#endif


int g_mode = 1;

struct KcpContext{
	int sock_;
	ikcpcb *kcp_;
	struct sockaddr_in remote_;
};

int udp_output(const char *buf, int len, ikcpcb *kcp, void *user)
{
	KcpContext *c = (KcpContext*)user;

	// return sendto(c->sock_, buf, len, 0, (const sockaddr*)&c->remote_, sizeof(struct sockaddr_in));
	printf("udp_output %d\n",len);
	if(0 == g_mode){
		return send(c->sock_, buf, len, 0);
	}
	else{
		return sendto(c->sock_, buf, len, 0, (const sockaddr*)&c->remote_, sizeof(struct sockaddr_in));
	}
}

void log_cb(const char *log, struct IKCPCB *kcp, void *user){
	printf(">>>> %s\n",log);
}


static void *s_arena = NULL;

void* __alloca(size_t n){
	void *p = myAlloca(s_arena, (n/4)+1);
	printf("alloca  %d  %p  %p\n", n, s_arena, p);
	return p;
}

void __dealloca(void* a){
	printf("dealloca  %p\n", a);
	myDealloca(s_arena, a);
}



int main(int argc, char** argv)
{
	if(argc<2) return 3;
	if('1' == argv[1][0]){
		g_mode = 1;
		printf("RUN IN SERVER MODE\n");
	}
	else{
		g_mode = 0;	// client mode
		printf("RUN IN CLIENT MODE\n");
	}

	s_arena = makeArena();

	ikcp_allocator(__alloca, __dealloca);

	

	int sockfd; 
	struct sockaddr_in servaddr, remote_addr;
	socklen_t addr_len = sizeof(struct sockaddr_in);

	sockfd = socket(AF_INET,SOCK_DGRAM,0);

	memset(&servaddr, 0, sizeof(servaddr)); 
	memset(&remote_addr, 0, sizeof(remote_addr));

	servaddr.sin_family=AF_INET; 
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
	servaddr.sin_port = htons(SERV_PORT);

	if(1 == g_mode){
		if(bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))==-1) { 
			perror("binderror"); 
		 	exit(1); 
		}
	}

	void* buffer1 = malloc(RECV_BUF_LEN);
	char* buffer2 = (char*)malloc(RECV_BUF_LEN);
	struct KcpContext kc;
	kc.sock_ = sockfd;

	kc.kcp_ = ikcp_create(KCP_CONV, &kc);
	ikcp_setoutput(kc.kcp_, udp_output);
	kc.kcp_->writelog = log_cb;

	if(0 == g_mode){
		kc.remote_.sin_family = AF_INET; 
		inet_pton(AF_INET, "127.0.0.1", &(kc.remote_.sin_addr));
		kc.remote_.sin_port = htons(SERV_PORT);

		if (connect(sockfd, (struct sockaddr *) &kc.remote_, sizeof(struct sockaddr)) < 0) {
	        close(sockfd);
	        exit(5); 
	    }
	}

	int count1 = 1;
	while(1){
		struct timeval time;
		gettimeofday(&time, NULL);
		uint now = time.tv_sec*1000 + time.tv_usec/1000;


		if(0 == g_mode){
			memset(buffer2, 0, RECV_BUF_LEN);
			int len2 = sprintf(buffer2, "message:%d", count1);
			++count1;
			int r3 = ikcp_send(kc.kcp_, buffer2, len2);
			printf("ikcp_send %d\n",r3);

			ikcp_update(kc.kcp_, now);
			ikcp_flush(kc.kcp_);
		}

		size_t n = recvfrom(sockfd, buffer1, RECV_BUF_LEN, 0, (sockaddr*)&remote_addr, &addr_len);
		//printf("get %d\n",n);

		if(n>0){
			memcpy(&kc.remote_, &remote_addr, addr_len);
			int r1 = ikcp_input(kc.kcp_, (char*)buffer1, n);
			//printf("r1 %d\n",r1);

			memset(buffer2, 0, RECV_BUF_LEN);
			int kn = ikcp_recv(kc.kcp_, (char*)buffer2, RECV_BUF_LEN);
			if(kn>0){
				printf("ikcp_recv:  %s\n",(char*)buffer2);
				if(1 == g_mode){
					ikcp_send(kc.kcp_, "ACK", 3);
				}
			}
		}
		

		
		ikcp_update(kc.kcp_, now);
	}

	return 0;
}
